const fetch = require('node-fetch');
const exec = require('child_process').exec;
const url = require('url');

var options;
var rpc;
async function getRpc() {
    if (rpc) {
        return rpc;
    }
    let libRpc = require('lib_rpc');
    await libRpc.rpc.setup();
    rpc = libRpc.rpc;
    return rpc;
}

function setup(props = {}) {
    options = {
        baseUri: props.baseUri || 'https://api.im.jpush.cn',
        baseUriReport: props.baseUriReport || 'https://report.im.jpush.cn/v2',
        smsUri: props.smsUri || 'https://api.sms.jpush.cn/v1',
        token: props.token || 'Nzk4MWIzNDI3MTNiZjFiNmNkMjY1NTM2OjhmZTdhYzJiN2NlZmI1OGJiMmRhMGUwNQ==',
    };
}

/**
 * 批量注册用户到极光IM 服务器，一次批量注册最多支持500个用户。
 * Example Request
 *  [{"username": "dev_fang", "password": "password"}] 
 * 
 * Response
 *  username
 *  error={code, message} 某个用户注册出错时，该对象里会有 error 对象，说明错误原因。
 *      899003 参数错误，Request Body参数不符合要求
 *      899001 用户已存在
 */
async function batchRegisterUsers(usersArr) {
    return await genericInvokeVia('v1/users/', 'POST', correctUser(usersArr));
}

async function registerUserOrUpdatePassword(username, password, extraParams = {}) {
    let resp = await batchRegisterUsers([{ username, password, nickname: extraParams.nickname, avatar: extraParams.avatar }]);
    if (!resp[0].error) {
        return resp[0];
    }
    if (resp[0].error.code == 899001) {
        return modifyUserPassword(username, password, extraParams);
    }
    return resp[0];
}

/**
 * 注册admin到极光IM 服务器
 * Example Request
 *  {"username": "dev_fang", "password": "password"} 
 * 
 * Response
 *  username
 *  error={code, message} 某个用户注册出错时，该对象里会有 error 对象，说明错误原因。
 *      899003 参数错误，Request Body参数不符合要求
 *      899001 用户已存在
 */
async function registerAdmins(usersArr) {
    return await genericInvokeVia('v1/admins/', 'POST', correctUser(usersArr));
}

/**
 * 获取用户信息
 * Response
 *  {
        "username" : "javen",
        "nickname" : "hello", 
        "avatar" : "/avatar", 
        "birthday" : "1990-01-24 00:00:00", 
        "gender" : 0, 
        "signature" : "orz", 
        "region" : "shenzhen", 
        "address" : "shenzhen", 
        "mtime" : "2015-01-01 00:00:00", 
        "ctime" : "2015-01-01 00:00:00"
 *  }
 * @param {*} usersArr 
 */
async function fetchUserInfo(username) {
    return await genericInvokeVia(`v1/users/${correctUser(username)}`, 'GET');
}

async function fetchUsersInfo(start, count) {
    return await genericInvokeVia(`v1/users/?start=${start}&count=${count}`, 'GET');
}

async function updateUserInfo(username, nickname, avatar) {
    return await genericInvokeVia(`v1/users/${correctUser(username)}`, 'PUT', {
        nickname,
        avatar
    });
}

/**
 * 用户在线状态查询
 * {
        "login": false,
        "online": false
    }
 */
async function fetchUserStatus(username) {
    return await genericInvokeVia(`v1/users/${correctUser(username)}/userstat`, 'GET');
}

async function modifyUserPassword(username, password) {
    return await genericInvokeVia(`v1/users/${correctUser(username)}/password`, 'PUT', {
        new_password: password
    });
}

async function deleteUser(username) {
    return await genericInvokeVia(`v1/users/${correctUser(username)}`, 'DELETE');
}

async function getBlockedUsers(username) {
    return await genericInvokeVia(`v1/users/${correctUser(username)}/blacklist`, 'GET');
}

/**
 * 添加黑名单
 * @param {*} username 
 */
async function blockUser(username, blackUsers) {
    return await genericInvokeVia(`v1/users/${correctUser(username)}/blacklist`, 'PUT', {
        blackUsers
    });
}

async function removeUserFromBlock(username, blackUsers) {
    return await genericInvokeVia(`v1/users/${correctUser(username)}/blacklist`, 'DELETE', {
        blackUsers
    });
}

/**  msg_type:text
{
    "version": 1, 
    "target_type": "single",
    "target_id": "javen",
    "from_type": "admin",
    "from_id": "fang", 
    "msg_type": "text",
    "msg_body": {
        "extras": {},
        "text": "Hello, JMessage!"  
    }
}

msg_type:image
{
    "version": 1, 
    "target_type": "single",
    "target_id": "javen",
    "from_type": "admin",
    "from_id": "fang", 
    "msg_type": "image",
    "msg_body": {
    "media_id": "qiniu/image/CE0ACD035CBF71F8",
    "media_crc32":2778919613,
    "width":3840,
    "height":2160,
    "fsize":3328738,
    "format":"jpg"
    }
}

msg_type:voice

{
    "version": 1, 
    "target_type": "single",
    "target_id": "ppppp",
    "from_type": "admin",
    "from_id": "admin_caiyh", 
    "msg_type": "voice",
    "msg_body": {
    "media_id": "qiniu/voice/j/A96B61EB3AF0E5CDE66D377DEA4F76B8",
    "media_crc32":1882116055,
    "hash":"FoYn15bAGRUM9gZCAkvf9dolVH7h",
    "fsize" :12344;
    "duration": 6
    }
}

msg_type:custom

{
    "version": 1, 
    "target_type": "single",
    "target_id": "ppppp",
    "from_type": "admin",
    "from_id": "admin_caiyh", 
    "msg_type": "voice",
    "msg_body": {
        json define yourself
    }
}*/
async function messageSend(msg) {
    msg.target_id = correctUser(msg.target_id);
    msg.from_id = correctUser(msg.from_id);
    return await genericInvokeVia('v1/messages', 'POST', msg);
}

/**
 * 文件下载
 * TODO:保存文件到指定路径
 */
async function messageMediaDownload(mediaId, fileName) {

    let resp = await genericInvokeVia(`v1/resource?mediaId=${mediaId}`, 'GET');
    if (resp.error) {
        return resp;
    }

    let downloadUrl = resp.url;
    await getRpc();
    let promisify = new Promise((resolve, reject) => {
        rpc.act('role: download, cmd: fetchUrlAndSave',
            {
                fileName, downloadUrl
            },
            (err, result) => {
                if (err) {
                    reject(err);
                    return;
                }
                resolve(result);
            });
    });
    return await promisify;
}

/**
 * 
 * @param {*} username 
 * @param {*} addsArr ["user01","user02"]
 */
async function addFriends(username, friends) {
    return await genericInvokeVia(`v1/users/${correctUser(username)}/friends`, 'POST', friends);
}

async function removeFriends(username, friends) {
    return await genericInvokeVia(`v1/users/${correctUser(username)}/friends`, 'DELETE', friends);
}

/**
 * 
 * @param {*} sensitiveWords ["FUCK"]
 */
async function addSensitiveword(sensitiveWords) {
    return await genericInvokeVia(`v1/sensitiveword`, 'POST', sensitiveWords);
}

/**
 * Request Params
    old_word 旧敏感词
    new_word 新敏感词
 */
async function modifySensitiveword(args) {
    return await genericInvokeVia(`v1/sensitiveword`, 'PUT', args);
}

async function deleteSensitiveword(sensitiveWords) {
    return await genericInvokeVia(`v1/sensitiveword`, 'DELETE', sensitiveWord);
}

async function isSensitivewordEnable(enable) {
    return await genericInvokeVia(`v1/sensitiveword/status`, 'GET', sensitiveWord);
}

async function setSensitivewordEnable(enable) {
    return await genericInvokeVia(`v1/sensitiveword/status?status=${enable ? 1 : 0}`, 'PUT', sensitiveWord);
}

async function fetchUserMessages(username, count, begin_time, end_time) {
    let result = await genericInvokeReporterVia(`/users/${correctUser(username)}/messages?count=${count}&begin_time=${begin_time}&end_time=${end_time}`, 'GET');
    console.log(`invoke /users/${correctUser(username)}/messages?count=${count}&begin_time=${begin_time}&end_time=${end_time} and got ${JSON.stringify(result)}`);
    return result;
}

async function sendSms(phoneNo, templateId = 1) {
    //TODO:
    console.log('sendSms was not used currently');
    return;
    // return await genericInvokeSmsApiVia('codes', 'POST', {
    //     mobile: phoneNo,
    //     temp_id: templateId
    // });
}

async function verifySms(msgId, code) {
    return await genericInvokeSmsApiVia(`codes/${msgId}/valid`, 'POST', {
        code
    });
}

async function hasError(result) {
    return result.error;
}

async function errorMessage(result) {
    return result.error.message;
}

async function genericInvokeVia(relUri, method, params = {}) {
    let resp = await fetch(`${options.baseUri}/${relUri}`, {
        method,
        headers: createCommonHeaders(),
        body: JSON.stringify(params)
    });
    if (resp.status == 204) {
        return {}
    }
    return await resp.json();
}

async function genericInvokeReporterVia(relUri, method, params = {}) {
    let resp = await fetch(`${options.baseUriReport}/${relUri}`, {
        method,
        headers: createCommonHeaders(),
        body: JSON.stringify(params)
    });
    return await resp.json();
}

async function genericInvokeSmsApiVia(relUri, method, params = {}) {
    let resp = await fetch(`${options.smsUri}/${relUri}`, {
        method,
        headers: createCommonHeaders(),
        body: JSON.stringify(params)
    });
    return await resp.json();
}

function createCommonHeaders() {
    return {
        Authorization: `Basic ${options.token}`,
        'Content-Type': 'application/json',
        'User-Agent': 'sa.nodesvc.jmessage-openapi'
    };
}

function correctUser(userData) {
    if (!(userData instanceof Array)) {
        if (userData instanceof Object) {
            userData.username = correctUserNameString(userData.username);
            return userData;
        }
        if ('number' == (typeof userData)) {
            return correctUserNameString(`${userData}`);
        }
        return correctUserNameString(userData);
    }
    let resultArr = [];
    for (let userItemData of userData) {
        resultArr.push(correctUser(userItemData));
    }
    return resultArr;
}

function correctUserNameString(usernameStr) {
    if ((typeof usernameStr == 'string') && usernameStr.startsWith('dca_jpush_')) {
        return usernameStr;
    }
    return `dca_jpush_${usernameStr}`;
}

setup({});//TODO:

module.exports = {
    batchRegisterUsers,
    registerUserOrUpdatePassword,
    registerAdmins,
    fetchUserInfo,
    fetchUsersInfo,
    updateUserInfo,
    fetchUserStatus,
    modifyUserPassword,
    deleteUser,
    getBlockedUsers,
    blockUser,
    removeUserFromBlock,
    messageSend,
    messageMediaDownload,
    addFriends,
    removeFriends,
    addSensitiveword,
    modifySensitiveword,
    deleteSensitiveword,
    isSensitivewordEnable,
    setSensitivewordEnable,
    fetchUserMessages,
    sendSms,
    verifySms
}